You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Overrides normalizeHTML to call the indent normalizer
src/utils/modules/resize.ts
Added MxResizeModule with no-op initializeEmbed
src/utils/formats.d.ts
Moved MxQuillModulesOptions interface here
src/ui/variables.scss
New shared SCSS variables for fonts, sizes, colors
src/ui/RichTextFormatStyle.scss
Generates CSS classes for class-mode formatting
src/ui/RichText.scss
Added VIDEO, IFRAME { pointer-events: none }
src/components/ModalDialog/Dialog.scss
New styles for the custom code editor overlay
src/RichText.xml
New styleDataFormat enumeration property
typings/RichTextProps.d.ts
Added StyleDataFormatEnum and styleDataFormat prop
src/__tests__/fonts.spec.ts
New unit tests for FontStyleAttributor / FontClassAttributor
src/__tests__/helpers.spec.ts
New unit tests for normalizeStyleAndClassAttribute
src/__tests__/customList.spec.ts
New unit tests for custom list blots
src/__tests__/RichText.spec.tsx
Added styleDataFormat: "inline" to default props
e2e/RichText.spec.js
Three new E2E tests for class mode
CHANGELOG.md
Added Unreleased entries for Added/Fixed/Changed
package.json
Swapped CodeMirror for highlight.js + react-scroll-sync; pinned Mendix version
Skipped (out of scope): pnpm-lock.yaml, e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png
Findings
🔶 Medium — registerCustomFormats mutates global Quill registry per instance
File:src/utils/customPluginRegisters.ts lines 33–70 Problem:Quill.register() is a global singleton mutation. registerCustomFormats is called inside each Editor's useLayoutEffect (see Editor.tsx line ~136). If two RichText widgets exist on the same page — one in inline mode and one in class mode — whichever mounts last overwrites the global attributors for all instances. The first widget will silently switch to the wrong format mode without re-rendering. Fix: Gate registration so it only runs once per format value (e.g. track which format was last registered in a module-level variable and skip if already registered), or document clearly that mixing inline and class mode widgets on the same page is unsupported and add a runtime warning:
🔶 Medium — Missing E2E snapshot baselines for class-mode tests
File:e2e/RichText.spec.js lines 46, 67 Problem: Two new toHaveScreenshot() calls reference classModeEditor.png and classModeViewCodeDialog.png, but neither file exists in e2e/RichText.spec.js-snapshots/. Playwright requires a committed baseline PNG. These tests will fail on the first CI run with "missing snapshot" rather than generating a useful baseline. Fix: Run pnpm e2edev -- --update-snapshots against the test project to generate the baselines, then commit the resulting PNG files alongside this PR.
🔶 Medium — serif font missing from SCSS $fonts map
File:src/ui/variables.scss lines 14–27 Problem:FONT_LIST in fonts.ts contains 13 fonts including { value: "serif", ... }. The $fonts SCSS map has only 12 entries — serif is absent. FontClassAttributor.add() appends a font-family-serif class to the DOM node, but RichTextFormatStyle.scss generates no .font-family-serif rule, so the font silently fails to apply in class mode. Fix: Add serif to the $fonts map:
⚠️ Low — Orphaned $color variable in RichTextFormatStyle.scss
File:src/ui/RichTextFormatStyle.scss line 3 Note:$color: #3d1466; is declared but never referenced in the file. #3d1466 already appears as the last entry in $colors in variables.scss. Safe to remove to avoid confusion.
File:src/components/ModalDialog/Dialog.scss lines 28–46 Note:.code-input and .code-preview are position: absolute, which requires a positioned ancestor to stack correctly. .code-editor-container has no position: relative. Their stacking will fall through to whatever ancestor happens to be positioned, likely the modal overlay. This may work coincidentally with the current markup but is fragile. Add position: relative to .code-editor-container.
⚠️ Low — Buffer textarea not hidden from assistive technology
File:src/components/ModalDialog/ViewCodeDialog.tsx line 729 Note: The disabled buffer <textarea> is an invisible overlay used purely for scroll-sync height measurement. It will still be read by screen readers as a disabled form field. Add aria-hidden="true" to exclude it from the accessibility tree.
Positives
Removing the CodeMirror dependency (and its four transitive packages) meaningfully reduces bundle size for a CSP-sensitive use case.
The normalizeStyleAndClassAttribute utility is bidirectional — it handles both inline→class and class→inline conversion in a single function, which is clean and testable.
The three new unit test files use JSDOM directly without needing a Quill instance, making them fast and reliable. Good coverage of edge cases (RTL padding-right, zero-indent skip, custom fonts).
Moving side-effectful Quill.register() calls out of fontsize.ts import time is the right direction — modules that register globals on import are hard to test and control.
The useEffect cleanup in ViewCodeDialog.tsx correctly captures codeRef.current before the async gap and uses delete codeElement.dataset.highlighted to let highlight.js re-highlight on the next render.
New file — CSS class rules for color/font/size in class mode
src/ui/variables.scss
New file — font/color/size variables extracted here
src/__tests__/customList.spec.ts
New unit tests for CustomListItem.formats and CustomListItemClass
src/__tests__/fonts.spec.ts
New unit tests for FontStyleAttributor and FontClassAttributor
src/__tests__/helpers.spec.ts
New unit tests for normalizeStyleAndClassAttribute
src/__tests__/RichText.spec.tsx
Added styleDataFormat: "inline" to default props
e2e/RichText.spec.js
New tests for class-mode rendering and code dialog
CHANGELOG.md
Entry added for Added/Fixed/Changed
package.json
Replaced CodeMirror with highlight.js + react-scroll-sync; pinned Mendix version for E2E
Skipped (out of scope): pnpm-lock.yaml, e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png
Findings
🔶 Medium — Missing screenshot baselines for two new E2E toHaveScreenshot tests
File:e2e/RichText.spec.js lines 46 and 67 Problem: Two new tests call toHaveScreenshot('classModeEditor.png') and toHaveScreenshot('classModeViewCodeDialog.png'), but neither baseline PNG exists in e2e/RichText.spec.js-snapshots/. Playwright fails with "missing snapshot" when no baseline is present. These tests will block CI on first run. Fix: Run pnpm e2edev against the test project to generate the baselines, then commit the two PNG files:
🔶 Medium — Quill.register is a global singleton; two widgets with different styleDataFormat will conflict
File:src/utils/customPluginRegisters.ts lines 50–80; src/components/Editor.tsx line 140 Problem:registerCustomFormats calls Quill.register(...) on the global Quill registry inside each editor's useLayoutEffect. If a page contains two RichText widgets — one in inline mode and one in class mode — the second widget to mount overwrites the format registrations for both. The last-registered attributor wins globally (e.g. FontClassAttributor overwrites FontStyleAttributor even for the inline widget). Fix: Guard against re-registration using a module-level flag, or check the current registered format before calling Quill.register again. Example:
Note: even with this guard the first-registered mode wins for the whole page, which may still be wrong if two widgets have different modes. The underlying issue is that Quill's global registry does not support per-instance format variants. This is worth documenting as a known limitation in the PR description.
File:src/ui/RichTextFormatStyle.scss line 3 Note:$color: #3d1466; at the top of the file is never used — the last entry in the $colors list in variables.scss is already #3d1466. It also shadows the $color iteration variable in the @each $color in $colors loop on line 21 (SCSS loop variables are locally scoped, so this is not a bug, but it is confusing). Remove the unused variable.
⚠️ Low — Buffer textarea for scroll sync lacks aria-hidden
File:src/components/ModalDialog/ViewCodeDialog.tsx lines 64–70 Note: The disabled buffer textarea (used purely for scroll-sync measurement) is hidden via opacity: 0 in CSS, but it has no aria-hidden="true". Screen readers may still traverse to it and announce the duplicated content. Add aria-hidden="true" to the element.
File:src/ui/RichText.scss lines 175–176 Note:VIDEO, IFRAME { pointer-events: none; } uses uppercase selectors. While valid CSS, all other selectors in the codebase use lowercase. Change to video, iframe for consistency with the rest of the stylesheet.
File:e2e/RichText.spec.js line 32 Note: Placing .configure({ mode: "serial" }) at the outer test.describe("RichText", ...) level serialises all tests in that block, including the pre-existing parallel tests. If the intent was only to serialise the new class-mode tests, extract them into a nested test.describe("class mode", ...) block and apply the config there. Otherwise this will slow down CI for all RichText E2E runs.
Positives
Excellent unit test coverage for the new utility classes (FontClassAttributor, normalizeStyleAndClassAttribute, CustomListItem.formats) — tests cover edge cases like RTL padding, zero-indent, custom fonts, and unknown font values.
Replacing CodeMirror (a heavy dependency with CSP-incompatible new Function evaluation) with a plain <textarea> + highlight.js is the right call for CSP compliance and bundle size.
normalizeStyleAndClassAttribute handles bidirectional conversion (class→inline and inline→class) so pasted content is normalised regardless of the source editor mode.
Moving side-effectful module-level Quill.register calls out of fontsize.ts and into registerCustomFormats is a clean improvement — module imports no longer have hidden global side effects.
CHANGELOG.md entry is present and correctly covers all three change categories (Added, Fixed, Changed).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
use class attributor instead of style attributor to avoid inline styling